import path from 'path'
import { Plugin } from 'rollup'
import { getHelper as getAssemblyScriptHelper } from './helpers/assemblyScript'
import { getHelper as getBaseHelper } from './helpers/base'
import { getHelper as getEmscriptenHelper } from './helpers/emscripten'
import { getHelper as getGolangHelper } from './helpers/golang'
import { getHelper as getWebAssemblyHelper } from './helpers/webAssembly'
import { resolveOptions } from './options'
import { UserOptions } from './types'
import { getRelativeUrlFromDocument, getResolveUrl } from './utils'

export * from './types'

export default function wasm(options?: UserOptions): Plugin {
  const resolvedOptions = resolveOptions(options)

  const baseHelper = getBaseHelper(resolvedOptions)
  const webAssemblyHelper = getWebAssemblyHelper(resolvedOptions)
  const assemblyScriptHelper = getAssemblyScriptHelper(resolvedOptions)
  const emscriptenHelper = getEmscriptenHelper(resolvedOptions)
  const golangHelper = getGolangHelper(resolvedOptions)

  //@ts-ignore
  let viteConfig: import('vite').ResolvedConfig

  return {
    name: 'wasm',
    async configResolved(...args: any[]) {
      viteConfig = args[0]
      await baseHelper?.configResolved.apply(this, args as any)
    },
    async configureServer(...args: any[]) {
      await baseHelper?.configureServer.apply(this, args as any)
    },
    async resolveId(...args) {
      return (
        (await baseHelper?.resolveId.apply(this, args)) ??
        (await assemblyScriptHelper?.resolveId.apply(this, args)) ??
        (await emscriptenHelper?.resolveId.apply(this, args)) ??
        (await golangHelper?.resolveId.apply(this, args))
      )
    },
    async load(...args) {
      return (
        (await baseHelper?.load.apply(this, args)) ??
        (await webAssemblyHelper?.load.apply(this, args)) ??
        (await assemblyScriptHelper?.load.apply(this, args)) ??
        (await emscriptenHelper?.load.apply(this, args)) ??
        (await golangHelper?.load.apply(this, args))
      )
    },
    generateBundle(...args) {
      baseHelper?.generateBundle.apply(this, args)
      emscriptenHelper?.generateBundle.apply(this, args)
    },
    resolveFileUrl(options) {
      let { relativePath, format, fileName } = options

      if (viteConfig) {
        let p = path.posix.join(viteConfig.base, fileName)
        if (!p.startsWith('/') && !/^\.[\.\/]/.test(p) && !p.includes(':')) {
          p = './' + p
        }
        return JSON.stringify(p)
      }

      switch (format) {
        case 'amd':
          if (/^\.[\/\\\.]/.test(relativePath)) relativePath = './' + relativePath
          return getResolveUrl(`require.toUrl('${relativePath}'), document.baseURI`)
        case 'cjs':
          if (resolvedOptions.target === 'node') {
            return `(__dirname + '/${relativePath}')`
          } else {
            return `(typeof document === 'undefined' ? ${getResolveUrl(
              `'file:' + __dirname + '/${relativePath}'`,
              `(require('u' + 'rl').URL)`
            )} : ${getRelativeUrlFromDocument(relativePath)})`
          }
        case 'es':
          if (resolvedOptions.target === 'node') {
            return `(import.meta.url.replace('file:///', '') + '/../${relativePath}')`
          } else {
            return getResolveUrl(`'${relativePath}', import.meta.url`)
          }
        case 'iife':
          return getRelativeUrlFromDocument(relativePath)
        case 'system':
          return getResolveUrl(`'${relativePath}', module.meta.url`)
        case 'umd':
          if (resolvedOptions.target === 'node') {
            return `(__dirname + '/${relativePath}')`
          } else {
            return `(typeof document === 'undefined' && typeof location === 'undefined' ? ${getResolveUrl(
              `'file:' + __dirname + '/${relativePath}'`,
              `(require('u' + 'rl').URL)`
            )} : ${getRelativeUrlFromDocument(relativePath, true)})`
          }
      }
    },
  } as Plugin & Record<string, any>
}
